Tutki tehokkaita TypeScript-vaihtoehtoja enumeille: const-väitteet ja union-tyypit. Opi, milloin käyttää mitäkin kestävän ja ylläpidettävän koodin luomiseksi.
Enumeiden tuolla puolen: TypeScriptin const-väitteet vs. union-tyypit
Stattisesti tyypitetyn JavaScriptin maailmassa TypeScriptillä enumit ovat pitkään olleet käytännöllinen ratkaisu kiinteän joukon nimettyjä vakioita edustamaan. Ne tarjoavat selkeän ja luettavan tavan määritellä joukko toisiinsa liittyviä arvoja. Kuitenkin, kun projektit kasvavat ja kehittyvät, kehittäjät etsivät usein joustavampia ja joskus tehokkaampia vaihtoehtoja. Kaksi tehokasta ehdokasta, jotka usein nousevat esiin, ovat const-väitteet ja union-tyypit. Tämä postaus perehtyy näiden vaihtoehtojen käyttöön perinteisille enumeille, tarjoten käytännön esimerkkejä ja opastaen sinua valitsemaan oikean vaihtoehdon.
Perinteisten TypeScript-enumeiden ymmärtäminen
Ennen kuin tutustumme vaihtoehtoihin, on tärkeää ymmärtää, miten tavalliset TypeScript-enumit toimivat. Enumit antavat sinun määritellä joukon nimettyjä numeerisia tai merkkijono vakioita. Ne voivat olla numeerisia (oletus) tai merkkijonopohjaisia.
Numeeriset enumit
Oletuksena enum-jäsenille määritetään numeeriset arvot alkaen arvosta 0.
enum DirectionNumeric {
Up,
Down,
Left,
Right
}
let myDirection: DirectionNumeric = DirectionNumeric.Up;
console.log(myDirection); // Output: 0
Voit myös määrittää numeerisia arvoja eksplisiittisesti.
enum StatusCode {
Success = 200,
NotFound = 404,
InternalError = 500
}
let responseStatus: StatusCode = StatusCode.Success;
console.log(responseStatus); // Output: 200
Merkkijonoenumit
Merkkijonoenumit ovat usein suositeltavia niiden parantuneen virheenkorjauskokemuksen vuoksi, koska jäsenten nimet säilytetään käännetyissä JavaScript-tiedostoissa.
enum ColorString {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
let favoriteColor: ColorString = ColorString.Blue;
console.log(favoriteColor); // Output: "BLUE"
Enumeiden lisäkustannukset
Vaikka enumit ovat käteviä, niillä on pieni lisäkustannus. Käännettynä JavaScriptiksi, TypeScript-enumit muutetaan objekteiksi, joilla on usein käänteiskuvauksia (esim. numeerisen arvon kartoittaminen takaisin enum-nimeen). Tämä voi olla hyödyllistä, mutta se myös vaikuttaa paketin kokoon eikä välttämättä ole aina tarpeen.
Harkitse tätä yksinkertaista merkkijonoenumia:
enum Status {
Pending = "PENDING",
Processing = "PROCESSING",
Completed = "COMPLETED"
}
JavaScriptissä tästä voi tulla jotain tällaista:
var Status;
(function (Status) {
Status["Pending"] = "PENDING";
Status["Processing"] = "PROCESSING";
Status["Completed"] = "COMPLETED";
})(Status || (Status = {}));
Yksinkertaisille, vain luku -vakiojoukoille tämä luotu koodi voi tuntua hieman liialliselta.
Vaihtoehto 1: Const-väitteet
Const-väitteet ovat tehokas TypeScript-ominaisuus, jonka avulla voit kertoa kääntäjälle johtaa mahdollisimman tarkka tyyppi arvolle. Käytettynä taulukoiden tai objektien kanssa, joiden on tarkoitus edustaa kiinteää arvojoukkoa, ne voivat toimia kevyenä vaihtoehtona enumeille.
Const-väitteet taulukoilla
Voit luoda taulukon merkkijonoliteraaleista ja käyttää sitten const-väitettä tekemään sen tyypistä muuttumaton ja sen elementeistä literaalityyppejä.
const statusArray = ["PENDING", "PROCESSING", "COMPLETED"] as const;
type StatusType = typeof statusArray[number];
let currentStatus: StatusType = "PROCESSING";
// currentStatus = "FAILED"; // Error: Type '"FAILED"' is not assignable to type 'StatusType'.
function processStatus(status: StatusType) {
console.log(`Processing status: ${status}`);
}
processStatus("COMPLETED");
Katsotaanpa, mitä tässä tapahtuu:
as const: Tämä väite kertoo TypeScriptille käsitellä taulukkoa vain luku -tilassa ja johtaa sen elementtien tarkimmat literaalityypit. Joten sen sijaan, ettästring[], tyypistä tuleereadonly ["PENDING", "PROCESSING", "COMPLETED"].typeof statusArray[number]: Tämä on kartoitettu tyyppi. Se iteroi kaikkienstatusArray-taulukon indeksien läpi ja poimii niiden literaalityypit.number-indeksi allekirjoitus olennaisesti sanoo "anna minulle tämän taulukon minkä tahansa elementin tyyppi." Tulos on union-tyyppi:"PENDING" | "PROCESSING" | "COMPLETED".
Tämä lähestymistapa tarjoaa tyyppiturvallisuuden, joka on samanlainen kuin merkkijonoenumit, mutta luo minimaalisen JavaScriptin. statusArray itsessään pysyy merkkijonojen taulukkona JavaScriptissä.
Const-väitteet objekteilla
Const-väitteet ovat vielä tehokkaampia, kun niitä sovelletaan objekteihin. Voit määrittää objektin, jossa avaimet edustavat nimettyjä vakioitasi ja arvot ovat literaalimerkkijonoja tai -numeroita.
const userRoles = {
Admin: "ADMIN",
Editor: "EDITOR",
Viewer: "VIEWER"
} as const;
type UserRole = typeof userRoles[keyof typeof userRoles];
let currentUserRole: UserRole = "EDITOR";
// currentUserRole = "GUEST"; // Error: Type '"GUEST"' is not assignable to type 'UserRole'.
function displayRole(role: UserRole) {
console.log(`User role is: ${role}`);
}
displayRole(userRoles.Admin); // Valid
displayRole("EDITOR"); // Valid
Tässä objektin esimerkissä:
as const: Tämä väite tekee koko objektista vain luku -tilassa. Vielä tärkeämpää on, että se johtaa literaalityypit kaikille ominaisuuksien arvoille (esim."ADMIN"merkkijonon sijaan) ja tekee ominaisuuksista itsestään vain luku -tilassa.keyof typeof userRoles: Tämä lauseke tuottaa unioninuserRoles-objektin avaimista, mikä on"Admin" | "Editor" | "Viewer".typeof userRoles[keyof typeof userRoles]: Tämä on haku-tyyppi. Se ottaa avainten unionin ja käyttää sitä vastaavien arvojen etsimiseenuserRoles-tyypissä. Tämä johtaa arvojen unioniin:"ADMIN" | "EDITOR" | "VIEWER", mikä on haluttu tyyppimme rooleille.
userRoles:in JavaScript-tuloste on tavallinen JavaScript-objekti:
var userRoles = {
Admin: "ADMIN",
Editor: "EDITOR",
Viewer: "VIEWER"
};
Tämä on huomattavasti kevyempi kuin tyypillinen enum.
Milloin käyttää const-väitteitä
- Vain luku -vakiot: Kun tarvitset kiinteän joukon merkkijono- tai numeroliteraaleja, jotka eivät saa muuttua ajon aikana.
- Minimaalinen JavaScript-tuloste: Jos olet huolissasi paketin koosta ja haluat tehokkaimman ajonaikaisen esityksen vakioillesi.
- Objektin kaltainen rakenne: Kun pidät avain-arvoparien luettavuudesta, samoin kuin tapa, jolla voit rakentaa dataa tai konfiguraatiota.
- Merkkijonopohjaiset joukot: Erityisen hyödyllisiä edustettaessa tiloja, tyyppejä tai kategorioita, jotka on parhaiten tunnistettavissa kuvaavilla merkkijonoilla.
Vaihtoehto 2: Union-tyypit
Union-tyypit antavat sinun ilmoittaa, että muuttuja voi sisältää arvon useista tyypeistä. Yhdistettynä literaalityyppeihin (merkkijono-, numero-, boolean-literaalit), ne muodostavat tehokkaan tavan määritellä sallittu arvojoukko ilman erillisen vakioilmoituksen tarvetta joukolle itselleen.
Union-tyypit merkkijonoliteraaleilla
Voit suoraan määritellä merkkijonoliteraalien unionin.
type TrafficLightColor = "RED" | "YELLOW" | "GREEN";
let currentLight: TrafficLightColor = "YELLOW";
// currentLight = "BLUE"; // Error: Type '"BLUE"' is not assignable to type 'TrafficLightColor'.
function changeLight(color: TrafficLightColor) {
console.log(`Changing light to: ${color}`);
}
changeLight("RED");
// changeLight("REDDY"); // Error
Tämä on suorin ja usein tiivein tapa määritellä joukko sallittuja merkkijonoarvoja.
Union-tyypit numeerisilla literaaleilla
Samoin voit käyttää numeerisia literaaleja.
type HttpStatusCode = 200 | 400 | 404 | 500;
let responseCode: HttpStatusCode = 404;
// responseCode = 201; // Error: Type '201' is not assignable to type 'HttpStatusCode'.
function handleResponse(code: HttpStatusCode) {
if (code === 200) {
console.log("Success!");
} else {
console.log(`Error code: ${code}`);
}
}
handleResponse(500);
Milloin käyttää union-tyyppejä
- Yksinkertaiset, suorat joukot: Kun sallittujen arvojen joukko on pieni, selkeä ja ei vaadi kuvaavia avaimia itse arvojen lisäksi.
- Implisiittiset vakiot: Kun sinun ei tarvitse viitata nimettyyn vakioon itse joukolle, vaan käytät suoraan literaaliarvoja.
- Maksimaalinen tiiveys: Suoraviivaisissa skenaarioissa, joissa erillisen objektin tai taulukon määrittäminen tuntuu liialliselta.
- Funktioparametrit/paluutyypit: Erinomainen määritettäessä tarkka joukko hyväksyttäviä merkkijono- tai numerotuloja/lähtöjä funktioille.
Enumeiden, const-väitteiden ja union-tyyppien vertailu
Kerrataan tärkeimmät erot ja käyttötapaukset:
Ajonaikainen käyttäytyminen
- Enumit: Luo JavaScript-objekteja, mahdollisesti käänteisillä kartoituksilla.
- Const-väitteet (taulukot/objektit): Luo tavallisia JavaScript-taulukoita tai -objekteja. Tyyppitiedot poistetaan ajon aikana, mutta tietorakenne säilyy.
- Union-tyypit (literaaleilla): Ei ajonaikaista esitystä unionille itselleen. Arvot ovat vain literaaleja. Tyyppitarkistus tapahtuu puhtaasti käännösajassa.
Luettavuus ja ilmaisukyky
- Enumit: Korkea luettavuus, erityisesti kuvaavilla nimillä. Voi olla verbosempi.
- Const-väitteet (objektit): Hyvä luettavuus avain-arvoparien kautta, jäljittelee konfiguraatioita tai asetuksia.
- Const-väitteet (taulukot): Vähemmän luettava nimettyjen vakioiden esittämiseen, enemmän vain arvojen järjestykseen.
- Union-tyypit: Erittäin tiivis. Luettavuus riippuu itse literaaliarvojen selkeydestä.
Tyyppiturvallisuus
- Kaikki kolme lähestymistapaa tarjoavat vahvan tyyppiturvallisuuden. Ne varmistavat, että vain kelvollisia, ennalta määritettyjä arvoja voidaan määrittää muuttujille tai välittää funktioille.
Paketin koko
- Enumit: Yleisesti ottaen suurin luotujen JavaScript-objektien vuoksi.
- Const-väitteet: Pienempi kuin enumit, koska ne tuottavat tavallisia tietorakenteita.
- Union-tyypit: Pienin, koska ne eivät luo mitään erityistä ajonaikaista tietorakennetta itse tyypille, vaan luottavat vain literaaliarvoihin.
Käyttötapausmatriisi
Tässä on pikainen opas:
| Ominaisuus | TypeScript Enum | Const-väite (objekti) | Const-väite (taulukko) | Union-tyyppi (literaalit) |
|---|---|---|---|---|
| Ajonaikainen tulos | JS-objekti (käänteiskartoituksella) | Tavallinen JS-objekti | Tavallinen JS-taulukko | Ei mitään (vain literaaliarvot) |
| Luettavuus (nimetyt vakiot) | Korkea | Korkea | Keskitaso | Matala (arvot ovat nimiä) |
| Paketin koko | Suurin | Keskitaso | Keskitaso | Pienin |
| Joustavuus | Hyvä | Hyvä | Hyvä | Erinomainen (yksinkertaisille joukoille) |
| Yleinen käyttö | Tilojen, tila koodien, kategoriat | Konfiguraatio, roolimäärittelyt, ominaisuusliput | Muuttumattomien arvojen järjestykset | Funktioparametrit, yksinkertaiset rajoitetut arvot |
Käytännön esimerkkejä ja parhaita käytäntöjä
Esimerkki 1: API-tilakoodien esittäminen
Enum:
enum ApiStatus {
Success = "SUCCESS",
Error = "ERROR",
Pending = "PENDING"
}
function handleApiResponse(status: ApiStatus) {
// ... logiikka ...
}
Const-väite (objekti):
const apiStatusCodes = {
SUCCESS: "SUCCESS",
ERROR: "ERROR",
PENDING: "PENDING"
} as const;
type ApiStatus = typeof apiStatusCodes[keyof typeof apiStatusCodes];
function handleApiResponse(status: ApiStatus) {
// ... logiikka ...
}
Union-tyyppi:
type ApiStatus = "SUCCESS" | "ERROR" | "PENDING";
function handleApiResponse(status: ApiStatus) {
// ... logiikka ...
}
Suositus: Tässä skenaariossa union-tyyppi on usein tiivein ja tehokkain. Literaaliarvot itsessään ovat riittävän kuvaavia. Jos sinun on liitettävä lisämetatietoja jokaiseen tilaan (esim. käyttäjäystävällinen viesti), const-väiteobjekti olisi parempi valinta.
Esimerkki 2: Käyttäjäroolien määrittäminen
Enum:
enum UserRoleEnum {
Admin = "ADMIN",
Moderator = "MODERATOR",
User = "USER"
}
function getUserPermissions(role: UserRoleEnum) {
// ... logiikka ...
}
Const-väite (objekti):
const userRolesObject = {
Admin: "ADMIN",
Moderator: "MODERATOR",
User: "USER"
} as const;
type UserRole = typeof userRolesObject[keyof typeof userRolesObject];
function getUserPermissions(role: UserRole) {
// ... logiikka ...
}
Union-tyyppi:
type UserRole = "ADMIN" | "MODERATOR" | "USER";
function getUserPermissions(role: UserRole) {
// ... logiikka ...
}
Suositus: Const-väiteobjekti saavuttaa tässä hyvän tasapainon. Se tarjoaa selkeät avain-arvoparit (esim. userRolesObject.Admin), mikä voi parantaa luettavuutta viitattaessa rooleihin, samalla kun se on edelleen suorituskykyinen. Union-tyyppi on myös erittäin vahva haastaja, jos suorat merkkijonoliteraalit riittävät.
Esimerkki 3: Konfiguraatioasetusten esittäminen
Kuvittele konfiguraatioobjekti globaalille sovellukselle, jolla voi olla eri teemoja.
Enum:
enum Theme {
Light = "light",
Dark = "dark",
System = "system"
}
interface AppConfig {
theme: Theme;
// ... muut konfigurointiasetukset ...
}
Const-väite (objekti):
const themes = {
Light: "light",
Dark: "dark",
System: "system"
} as const;
type Theme = typeof themes[keyof typeof themes];
interface AppConfig {
theme: Theme;
// ... muut konfigurointiasetukset ...
}
Union-tyyppi:
type Theme = "light" | "dark" | "system";
interface AppConfig {
theme: Theme;
// ... muut konfigurointiasetukset ...
}
Suositus: Konfigurointi asetuksille, kuten teemoille, const-väiteobjekti on usein ihanteellinen. Se määrittelee selkeästi saatavilla olevat vaihtoehdot ja niiden vastaavat merkkijonoarvot. Avaimet (Light, Dark, System) ovat kuvaavia ja kartoittavat suoraan arvoihin, mikä tekee konfiguraatiokoodista erittäin ymmärrettävän.
Oikean työkalun valitseminen työhön
Päätös TypeScript-enumeiden, const-väitteiden ja union-tyyppien välillä ei ole aina mustavalkoinen. Se riippuu usein kompromissista ajonaikaisen suorituskyvyn, paketin koon ja koodin luettavuuden/ilmaisukyvyn välillä.
- Valitse Union-tyypit, kun tarvitset yksinkertaisen, rajoitetun joukon merkkijono- tai numeroliteraaleja ja haluat maksimaalisen tiiviyden. Ne ovat erinomaisia funktiosignatuureille ja perusarvorajoituksille.
- Valitse Const-väitteet (objekteilla), kun haluat rakenteisemman, luettavamman tavan määrittää nimettyjä vakioita, samoin kuin enum, mutta huomattavasti vähemmän ajonaikaista ylikuormitusta. Tämä on hienoa konfiguraatioille, rooleille tai mille tahansa joukolle, jossa avaimet lisäävät merkitystä.
- Valitse Const-väitteet (taulukoilla), kun tarvitset vain muuttumattoman järjestyksen arvojen listan, ja suora pääsy indeksin kautta on tärkeämpää kuin nimetyt avaimet.
- Harkitse TypeScript-enumeita, kun tarvitset niiden erityisominaisuuksia, kuten käänteiskartoitusta (vaikka tämä on vähemmän yleistä nykyaikaisessa kehityksessä) tai jos tiimilläsi on vahva mieltymys ja suorituskykyvaikutus on merkityksetön projektillesi.
Monissa nykyaikaisissa TypeScript-projekteissa huomaat kallistumista const-väitteisiin ja union-tyyppeihin perinteisten enumeiden sijaan, erityisesti merkkijonopohjaisille vakioille, niiden parempien suorituskykyominaisuuksien ja usein yksinkertaisemman JavaScript-tulostuksen vuoksi.
Globaalit näkökohdat
Kehitettäessä sovelluksia globaalille yleisölle, johdonmukaiset ja ennustettavat vakiomääritykset ovat ratkaisevan tärkeitä. Valinnat, joista olemme keskustelleet (enumit, const-väitteet, union-tyypit), kaikki edistävät tätä johdonmukaisuutta vahvistamalla tyyppiturvallisuutta eri ympäristöissä ja kehittäjien sijainneissa.
- Johdonmukaisuus: Valitusta menetelmästä riippumatta, avain on johdonmukaisuus projektissasi. Jos päätät käyttää const-väiteobjekteja rooleille, pidä kiinni tästä kuviosta koko koodikannassa.
- Kansainvälistäminen (i18n): Kun määrität tunnisteita tai viestejä, jotka kansainvälistetään, käytä näitä tyyppiturvallisia rakenteita varmistaaksesi, että käytetään vain kelvollisia avaimia tai tunnisteita. Todelliset käännetyt merkkijonot hallitaan erikseen i18n-kirjastojen kautta. Esimerkiksi, jos sinulla on
tila-kenttä, joka voi olla "PENDING", "PROCESSING", "COMPLETED", i18n-kirjastosi kartoittaisi nämä sisäiset tunnisteet lokalisoituun näyttötekstiin. - Aikavyöhykkeet & Valuutat: Vaikka ei suoraan liity enumeihin, muista, että käsitellessäsi arvoja, kuten päivämääriä, aikoja tai valuuttoja, TypeScriptin tyyppijärjestelmä voi auttaa varmistamaan oikean käytön, mutta ulkoiset kirjastot ovat yleensä tarpeen tarkkaan globaaliin käsittelyyn. Esimerkiksi,
Valuutta-union-tyyppi voitaisiin määritellä "USD" | "EUR" | "GBP", mutta varsinainen muunnoslogiikka vaatii erikoistuneita työkaluja.
Johtopäätös
TypeScript tarjoaa runsaan valikoiman työkaluja vakioiden hallintaan. Vaikka enumit ovat palvelleet meitä hyvin, const-väitteet ja union-tyypit tarjoavat houkuttelevia, usein tehokkaampia, vaihtoehtoja. Ymmärtämällä niiden erot ja valitsemalla oikean lähestymistavan erityistarpeidesi mukaan - oli se sitten suorituskyky, luettavuus tai tiiveys - voit kirjoittaa kestävää, ylläpidettävää ja tehokasta TypeScript-koodia, joka skaalautuu globaalisti.
Näiden vaihtoehtojen omaksuminen voi johtaa pienempiin pakettikokoihin, nopeampiin sovelluksiin ja ennustettavampaan kehittäjäkokemukseen kansainväliselle tiimillesi.